import { consts } from "@/module-00-codebase/pkg-00-const";
import { Bi } from "@/module-00-codebase/pkg-01-container";
import { BaseElmt } from "@/module-00-codebase/pkg-03-abstract";

export class Broker {
  public read(propertyPath: string): any;
  public read(propertyPath: string, subscribeFuncKey: any, subscribeFunc: () => void): any;
  public read(propertyPath: string, subscribeFuncKey?: any, subscribeFuncCall?: () => void): any {
    let properties: string[];
    if (currentBindingElmt === null && !(subscribeFuncKey && subscribeFuncCall)) {
      // skip
    } else if ((properties = Bi.utils.splitProperties(propertyPath)).length === 0) {
      throw new Error("Cannot read value from Broker with an empty key.");
    } else {
      let parent = map;
      let node: SubscribeNode;
      for (let i = 0, length = properties.length; i < length; i++) {
        const tempNode = parent.get(properties[i]);
        if (!tempNode) {
          node = { key: properties[i], elmtSet: new Set(), functionMap: new Map(), leafs: new Map() };
          parent.set(properties[i], node);
        } else {
          node = tempNode;
        }
        parent = node.leafs;
        if (i === length - 1) {
          if (subscribeFuncKey && subscribeFuncCall) {
            node.functionMap.set(subscribeFuncKey, subscribeFuncCall);
          } else if (currentBindingElmt) {
            node.elmtSet.add(currentBindingElmt);
          }
        }
      }
      return Bi.utils.readValue(cache, propertyPath);
    }
  }
  public write(propertyPath: string, value: any) {
    Bi.utils.writeValue(cache, propertyPath, value);
    const keys = Bi.utils.splitProperties(propertyPath);
    const elmtSet = new Set<BaseElmt<any>>();
    const funcSet = new Set<() => void>();
    let parent = map;
    let nodeOfKey: SubscribeNode | undefined = undefined;
    for (let i = 0, length = keys.length; i < length; i++) {
      const node = parent.get(keys[i]);
      if (!node) {
        break;
      } else {
        addToSet(node, elmtSet, funcSet);
        parent = node.leafs;
      }
      if (i === keys.length - 1) nodeOfKey = node;
    }
    if (!!nodeOfKey) addToSetCascade(nodeOfKey, elmtSet, funcSet);
    funcSet.forEach(func => func.call(null));
    elmtSet.forEach(elmt => elmt.elmtRefresh());
  }
}
export const initializing = async () => {
  const oldRender = BaseElmt.prototype.render;
  const newRender = function (this: BaseElmt<any>) {
    const oriElmt = currentBindingElmt;
    currentBindingElmt = this;
    try {
      return oldRender.call(this);
    } finally {
      currentBindingElmt = oriElmt;
    }
  };
  BaseElmt.prototype.render = newRender;
};
export const order = () => consts.firstOrder;

/**
 * 将该[数据节点node]及其子孙挂载的[视图节点elmtSet]和[回调函数functionMap]加入 elmtSet 和 funcSet
 */
const addToSetCascade = (node: SubscribeNode, elmtSet: Set<BaseElmt<any, any>>, funcSet: Set<() => void>) => {
  addToSet(node, elmtSet, funcSet);
  node.leafs.forEach(node => addToSetCascade(node, elmtSet, funcSet));
};
/**
 * 将该[数据节点node]挂载的[视图节点elmtSet]和[回调函数functionMap]加入 elmtSet 和 funcSet
 *  */
const addToSet = (node: SubscribeNode, elmtSet: Set<BaseElmt<any, any>>, funcSet: Set<() => void>) => {
  node.elmtSet.forEach(elmt => (elmt.isDestroyed ? node.elmtSet.delete(elmt) : elmtSet.add(elmt)));
  node.functionMap.forEach(func => funcSet.add(func));
};
const cache = {} as any;
const map = new Map<string, SubscribeNode>();
let currentBindingElmt: BaseElmt<any> | null = null;

type SubscribeNode = { key: string; elmtSet: Set<BaseElmt<any>>; functionMap: Map<any, () => void>; leafs: Map<string, SubscribeNode> };
